#include <stdio.h> 
#include <stdlib.h>
#include "cola.h"


typedef struct _nodo_cola{
	void* dato;
	struct _nodo_cola* siguiente;
}nodo_cola;

struct cola{
	nodo_cola* primero;
	nodo_cola* ultimo;
	size_t cantidad;
};

nodo_cola* nodo_cola_crear(void* dato)
{
	nodo_cola* nodo = malloc(sizeof(nodo_cola));
	if(nodo == NULL)
		return NULL;
	nodo->dato = dato;
	nodo->siguiente = NULL;
	return nodo;	
}

/* *****************************************************************
 *                    IMPLEMENTACION DE LA COLA
 * *****************************************************************/

// Crea una cola.
// Post: devuelve una nueva cola vacía.
cola_t* cola_crear(){
	cola_t* cola = malloc(sizeof(cola_t));
	if(!cola)
		return NULL;
	
	cola->cantidad = 0;
	cola->primero = NULL;
	cola->ultimo = NULL;
	
	return cola;
}

// Destruye la cola. Si se recibe la función destruir_dato por parámetro,
// para cada uno de los elementos de la cola llama a destruir_dato.
// Pre: la cola fue creada. destruir_dato es una función capaz de destruir
// los datos de la cola, o NULL en caso de que no se la utilice.
// Post: se eliminaron todos los elementos de la cola.
void cola_destruir(cola_t *cola, void destruir_dato(void*)){
	while(!cola_esta_vacia(cola))
	{	void* dato = cola_desencolar(cola); 
		if(destruir_dato != NULL)			
		{	destruir_dato(dato);	}	
	}
	
	free(cola);	
}

// Devuelve verdadero o falso, según si la cola tiene o no elementos encolados.
// Pre: la cola fue creada.
bool cola_esta_vacia(const cola_t *cola){
	return (cola->cantidad == 0);
}

// Agrega un nuevo elemento a la cola. Devuelve falso en caso de error.
// Pre: la cola fue creada.
// Post: se agregó un nuevo elemento a la cola, valor se encuentra al final
// de la cola.
bool cola_encolar(cola_t *cola, void* valor){
	nodo_cola* nodo = nodo_cola_crear(valor);
	if(!cola_esta_vacia(cola))
	{	cola->ultimo->siguiente = nodo;	}
	else{	cola->primero = nodo;}
	cola->ultimo = nodo;
	cola->cantidad += 1;
	return true;
}

// Obtiene el valor del primer elemento de la cola. Si la cola tiene
// elementos, se devuelve el valor del primero, si está vacía devuelve NULL.
// Pre: la cola fue creada.
// Post: se devolvió el primer elemento de la cola, cuando no está vacía.
void* cola_ver_primero(const cola_t *cola){
	if(cola_esta_vacia(cola))
		return NULL;
	return cola->primero->dato;
}

// Saca el primer elemento de la cola. Si la cola tiene elementos, se quita el
// primero de la cola, y se devuelve su valor, si está vacía, devuelve NULL.
// Pre: la cola fue creada.
// Post: se devolvió el valor del primer elemento anterior, la cola
// contiene un elemento menos, si la cola no estaba vacía.
void* cola_desencolar(cola_t *cola){
	void* dato = NULL;
	if(!cola_esta_vacia(cola))
	{	dato = cola_ver_primero(cola);
		nodo_cola* segundo = cola->primero->siguiente;	
		free(cola->primero);
		cola->primero = segundo;	
		cola->cantidad -= 1;	}
	return dato;
}
